#include "StdAfx.h"
#include "AsyncEventManager.h"
#include "AsyncEventCaller.h"

// #ifdef _DEBUG
// #define new DEBUG_NEW
// #undef THIS_FILE
// static char THIS_FILE[] = __FILE__;
// #endif

struct ASYNC_EVENT_PARAM
{
	BOOL bSync;
	__int64 nProcPtr;
	DWORD dwEvent;
	DWORD dwCallerID;
	DWORD dwParam;
	UINT nExpandedParamSize;
	unsigned char pExpandedParamBuffer[1];
};

CAsyncEventManager::CAsyncEventManager(void)
	: m_hTransferWnd(NULL)
	, m_nTransferMsg(0)
	, m_dwCallerIDSeed(1)
{
	InitializeCriticalSection(&m_AllocListCS);
}

CAsyncEventManager::~CAsyncEventManager(void)
{
	CAllocList::iterator it = m_AllocList.begin();

	for(;it != m_AllocList.end(); ++it)
	{
		delete[] (*it);
	}

	DeleteCriticalSection (&m_AllocListCS);
}

CAsyncEventManager& CAsyncEventManager::GetInstance()
{
	static CAsyncEventManager Inst;
	return Inst;
}

DWORD CAsyncEventManager::RegisterCaller(CAsyncEventCaller* pCaller)
{
	DWORD dwCallerID = m_dwCallerIDSeed;
	++m_dwCallerIDSeed;

	m_CallerMap[dwCallerID] = pCaller;
	return dwCallerID;
}

void CAsyncEventManager::UnregisterCaller(DWORD dwCallerID)
{
	m_CallerMap.erase(dwCallerID);
}

void CAsyncEventManager::CallAsyncEventProc(DWORD dwCallerID, __int64 nProcPtr, DWORD dwEvent, DWORD dwParam,  LPVOID pExpandedParam, UINT nExpandedParamSize, BOOL bSync)
{
	if(!IsWindow(m_hTransferWnd) || m_nTransferMsg == 0)
		return;

	if(pExpandedParam == NULL)
		nExpandedParamSize = 0;

	BOOL bDelete = TRUE;

	ASYNC_EVENT_PARAM *pEventParam = NULL;
	unsigned char* pParamBuffer = NULL;

	pParamBuffer = AllocParamBuffer(sizeof(ASYNC_EVENT_PARAM)-1+nExpandedParamSize);
	pEventParam = reinterpret_cast<ASYNC_EVENT_PARAM*>(pParamBuffer);
	pEventParam->bSync = bSync;
	pEventParam->nProcPtr = nProcPtr;
	pEventParam->dwEvent = dwEvent;
	pEventParam->dwCallerID = dwCallerID;
	pEventParam->dwParam = dwParam;
	pEventParam->nExpandedParamSize = nExpandedParamSize;
	if(nExpandedParamSize > 0)
	{
		memcpy(pEventParam->pExpandedParamBuffer, pExpandedParam, nExpandedParamSize);
	}

	LPARAM lParam = reinterpret_cast<LPARAM>(pEventParam);

	if(bSync)
	{
		SendMessage(m_hTransferWnd, m_nTransferMsg, 0, lParam);
	}
	else
	{
		bDelete = !PostMessage(m_hTransferWnd, m_nTransferMsg, 0, lParam);
	}

	if(bDelete)
	{
		FreeParamBuffer(pParamBuffer);
	}
}

CAsyncEventCaller* CAsyncEventManager::GetCaller(DWORD dwCallerID)
{
	CAsyncEventCallerMap::iterator it = m_CallerMap.find(dwCallerID);
	if(it != m_CallerMap.end())
	{
		return it->second;
	}
	else
	{
		return NULL;
	}
}

void CAsyncEventManager::ProcessTransferMsg(WPARAM wParam, LPARAM lParam)
{
	//ASYNCEVENTPROC pEventProc = NULL;//reinterpret_cast<ASYNCEVENTPROC>(wParam);
	DWORD dwParam = 0;//lParam;
	DWORD dwCallerID = 0;
	__int64 nProcPtr = 0;
	DWORD dwEvent = 0;
	LPVOID pExpandedParam = NULL;
	UINT nExpandedParamSize = 0;
	unsigned char* pParamBuffer = NULL;
	ASYNC_EVENT_PARAM* pEventParam = NULL;

	pParamBuffer = reinterpret_cast<unsigned char*>(lParam);

	pEventParam = reinterpret_cast<ASYNC_EVENT_PARAM*>(pParamBuffer);


	dwCallerID = pEventParam->dwCallerID;
	nProcPtr = pEventParam->nProcPtr;
	dwEvent = pEventParam->dwEvent;
	dwParam = pEventParam->dwParam;
	nExpandedParamSize = pEventParam->nExpandedParamSize;
	if(nExpandedParamSize > 0)
		pExpandedParam = pEventParam->pExpandedParamBuffer;
	else
		pExpandedParam = NULL;

	CAsyncEventCaller* pCaller = GetCaller(dwCallerID);
	
	if(pCaller)
	{
		pCaller->OnAsyncEvent(nProcPtr, dwEvent, dwParam, pExpandedParam, nExpandedParamSize);//EventProc(pCaller, dwParam, pExpandedParam, nExpandedParamSize);
	}

	if(pEventParam->bSync == FALSE)
	{
		FreeParamBuffer(pParamBuffer);
	}
}

unsigned char* CAsyncEventManager::AllocParamBuffer(UINT nSize)
{
	unsigned char* pBuffer = (unsigned char*) malloc(sizeof(unsigned char) * nSize);
	//unsigned char* pBuffer = new unsigned char[nSize];

	//THREAD_SAFE_ACCESS(m_AllocListCS);
	 
	EnterCriticalSection (&m_AllocListCS);
	m_AllocList.push_back(pBuffer);
	LeaveCriticalSection(&m_AllocListCS);

	return pBuffer;
}

void CAsyncEventManager::FreeParamBuffer(unsigned char* pBuffer)
{
	if (pBuffer)
	{
		free(pBuffer);
		pBuffer = NULL;
	}
	//delete[] pBuffer;

	//THREAD_SAFE_ACCESS(m_AllocListCS);

	EnterCriticalSection (&m_AllocListCS);
	m_AllocList.remove(pBuffer);
	LeaveCriticalSection(&m_AllocListCS);
}

